package org.cainiao.acl.core.component;

import org.cainiao.acl.core.annotation.CnACL;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configurers.AuthorizeHttpRequestsConfigurer;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.servlet.mvc.condition.PathPatternsRequestCondition;
import org.springframework.web.servlet.mvc.method.RequestMappingInfo;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;

import java.util.Set;
import java.util.function.BiConsumer;

/**
 * <br />
 * <p>
 * Author: Cai Niao(wdhlzd@163.com)<br />
 */
public class AclUtil {

    /*
     * 利用 RequestMappingHandlerMapping<br />
     * 通过扫描 Spring MVC 的 Controller 方法，找到有 @HaseScope 注解的入口方法<br />
     * 并利用 Spring MVC 的 @RequestMapping 的匹配规则来配置 OAuth2 Client 的授权<br />
     * TODO 但使用 Starter 的开发者不一定使用 Servlet，或者不一定使用 Spring MVC，有时间重构为更灵活的方式
     */
    public static AuthorizeHttpRequestsConfigurer<HttpSecurity>
        .AuthorizationManagerRequestMatcherRegistry customizeAuthorizeByAnnotation(
        RequestMappingHandlerMapping requestMappingHandlerMapping,
        AuthorizeHttpRequestsConfigurer<HttpSecurity>
            .AuthorizationManagerRequestMatcherRegistry authorizationManagerRequestMatcherRegistry,
        BiConsumer<AuthorizeHttpRequestsConfigurer<HttpSecurity>.AuthorizedUrl, CnACL> authorizedUrlConfigurer) {

        requestMappingHandlerMapping.getHandlerMethods().forEach((requestMappingInfo, handlerMethod) -> {
            CnACL cnACL = AnnotationUtils.findAnnotation(handlerMethod.getMethod(), CnACL.class);
            if (cnACL == null) {
                return;
            }
            String path = getPath(requestMappingInfo);
            if (path == null) {
                return;
            }
            Set<RequestMethod> methods = requestMappingInfo.getMethodsCondition().getMethods();
            /*
             * 添加一个 RequestMatcherEntry，其 entry 为 AuthorizationManager<RequestAuthorizationContext>
             * 即增加一个请求匹配规则，匹配的请求会被 AuthorizationManager 验证是否具有特定权限
             */
            AuthorizeHttpRequestsConfigurer<HttpSecurity>.AuthorizedUrl authorizedUrl = methods.isEmpty()
                ? authorizationManagerRequestMatcherRegistry.requestMatchers(path)
                : authorizationManagerRequestMatcherRegistry.requestMatchers(methods.iterator().next().asHttpMethod(), path);

            authorizedUrlConfigurer.accept(authorizedUrl, cnACL);
        });
        return authorizationManagerRequestMatcherRegistry;
    }

    public static String getPath(RequestMappingInfo requestMappingInfo) {
        // 如：@PostMapping("/aaa/bbb")，则 directPaths.iterator().next() 为 "/aaa/bbb"
        Set<String> directPaths = requestMappingInfo.getDirectPaths();
        if (!directPaths.isEmpty()) {
            return directPaths.iterator().next();
        }
        /*
         * 如：@GetMapping("/aaa/bbb/{id}")
         * 则 pathPatternsRequestCondition.getFirstPattern().getPatternString() 为 "/aaa/bbb{id}"
         */
        PathPatternsRequestCondition pathPatternsRequestCondition = requestMappingInfo.getPathPatternsCondition();
        return pathPatternsRequestCondition == null
            ? null : pathPatternsRequestCondition.getFirstPattern().getPatternString();
    }
}
